iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0

什麼是 MFA?為什麼需要?

MFA(Multi-Factor Authentication)是除了密碼以外,再加一道驗證,例如:

第一層 第二層 常見組合
Email + Password 簡訊驗證碼(SMS) 最常見、安全性高
Google Login Email 驗證連結 輕量級
密碼 + Authenticator App TOTP(如 Google Authenticator) Firebase 未原生支援,要另外串
Firebase 目前支援的 MFA 是「SMS 簡訊」或「Authenticator App (Beta)」,我們先從 最易上手的 SMS 版本 實作起。

在 Firebase Console 啟用 MFA(SMS)

  1. 進入 Firebase Console → Authentication → Sign-in Method
  2. 啟用「Phone / SMS」
  3. 往下滑 → 找到「Multi-factor Authentication」→ 按啟用

在前端綁定第二層驗證(Enroll MFA)

流程是:使用者登入後 → 進入「帳號安全」頁 → 綁定手機成為第二道防線

import {
  getAuth,
  PhoneAuthProvider,
  multiFactor,
  RecaptchaVerifier,
} from "firebase/auth";

export const enrollMFAWithSMS = async (phoneNumber) => {
  const auth = getAuth();
  const user = auth.currentUser;
  if (!user) throw new Error("尚未登入");

  // 建立 reCAPTCHA 驗證器
  const appVerifier = new RecaptchaVerifier("mfa-recaptcha-container", {}, auth);

  // 使用 PhoneAuthProvider 發送驗證碼
  const provider = new PhoneAuthProvider(auth);
  const verificationId = await provider.verifyPhoneNumber(phoneNumber, appVerifier);

  // 提醒前端等待使用者輸入簡訊驗證碼
  return verificationId; // 讓前端儲存,用於 verify
};

輸入驗證碼 → 完成綁定

import { PhoneAuthProvider, multiFactor } from "firebase/auth";

export const verifyMFAEnrollment = async (verificationId, smsCode) => {
  const auth = getAuth();
  const user = auth.currentUser;
  if (!user) throw new Error("尚未登入");

  const cred = PhoneAuthProvider.credential(verificationId, smsCode);
  const multiFactorUser = multiFactor(user);

  await multiFactorUser.enroll(cred, "My Phone Number");
  console.log("已成功綁定 MFA");
};

登入時如何觸發 MFA?

當使用者下一次登入時,Firebase 會自動丟出 錯誤 auth/multi-factor-auth-required,此時你必須再次發簡訊驗證碼來完成登入。

import {
  signInWithEmailAndPassword,
  PhoneAuthProvider,
  RecaptchaVerifier,
} from "firebase/auth";

export const signInWithMFA = async (email, password) => {
  const auth = getAuth();
  try {
    return await signInWithEmailAndPassword(auth, email, password);
  } catch (error) {
    if (error.code === "auth/multi-factor-auth-required") {
      const resolver = error.resolver; // 必須儲存,之後用來完成驗證

      const phoneInfoOptions = resolver.hints[0]; // 預設取第一個綁定的電話

      // 建立 reCAPTCHA 驗證器
      const appVerifier = new RecaptchaVerifier("mfa-recaptcha-container", {}, auth);

      const provider = new PhoneAuthProvider(auth);
      const verificationId = await provider.verifyPhoneNumber(phoneInfoOptions, appVerifier);

      // 回傳資訊,等待使用者輸入 code
      return { resolver, verificationId };
    } else {
      throw error;
    }
  }
};

// 完成登入的第二階段
export const completeMFASignIn = async (resolver, verificationId, smsCode) => {
  const cred = PhoneAuthProvider.credential(verificationId, smsCode);
  const userCredential = await resolver.resolveSignIn(cred);
  console.log("登入成功", userCredential);
  return userCredential;
};

上一篇
變更密碼前的使用者重新驗證
下一篇
進階版 MFA 實作:Email / Google 登入 + SMS 二階段驗證
系列文
不只是登入畫面!一起打造現代化登入系統30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言